package com.gusto.engine.semsim.neighborhood.storedistance;

import java.util.List;
import java.util.Properties;

import org.apache.log4j.Logger;

import com.gusto.engine.beans.model.Res;
import com.gusto.engine.semsim.Neighbor;
import com.gusto.engine.semsim.neighborhood.SemEventListener;
import com.gusto.engine.semsim.neighborhood.SemNeighborhoodService;
import com.opensymphony.oscache.base.Cache;
import com.opensymphony.oscache.general.GeneralCacheAdministrator;

/**
 * <p>This implementation stores the distance into the database whenever it is calculated,
 * and cleans all the distances when the context configuration is changed.</p>
 * 
 * @author amokrane.belloui@gmail.com
 */
public class SemanticNeighborhoodServiceImpl1 implements SemEventListener, SemNeighborhoodService {
	
	private Logger log = Logger.getLogger(getClass());
	
	private GeneralCacheAdministrator cacheAdministrator;
	private Cache cache;
	
	private SimilarityPersistenceDAO persistencePlugin = null;
	
	public SemanticNeighborhoodServiceImpl1() {
		super();
		
		// Cache Administrator
		Properties props = new Properties();
		props.put("cache.memory", true);
		props.put("cache.capacity", "10000");
		props.put("cache.unlimited.disk", true);
		props.put("cache.algorithm", "com.opensymphony.oscache.base.algorithm.LRUCache");
		cacheAdministrator = new GeneralCacheAdministrator(props); 
		
		// Cache
		cache = cacheAdministrator.getCache();
	}
	
	public void setCache(Cache cache) {
		this.cache = cache;
	}
		
	public void setPersistencePlugin(SimilarityPersistenceDAO persistencePlugin) {
		this.persistencePlugin = persistencePlugin;
	}
	
	/**
	 * {@inheritDoc}
	 */
	public void contextConfigChanged(String context) throws Throwable {
		if (persistencePlugin != null) {
			persistencePlugin.cleanDistances(context);
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	public void similarityCalculated(String context, Res resource1, Res resource2, Double sim) {
		if (persistencePlugin != null) {
			if (sim != null) {
				log.info("Updating similarity ctx:" + context + ", " + resource1 + "-" + resource2 + ", Similarity: " + sim);
				String id1 = resource1.toString();
				String id2 = resource2.toString();
				persistencePlugin.updateDistance(context, ""+id1 , ""+id2, sim);
			}
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<Neighbor> getNeighborhood(String context, String resource, Double minDistance, Integer maxNeighbors) {
		String key = "sim." + context + "." + resource;
		List<Neighbor> neighbors;
		
		if (persistencePlugin != null) {
			try {
				neighbors = (List<Neighbor>) cache.getFromCache(key);
				log.debug("Load List<Neighbor> from Cache");
			} 
			catch (com.opensymphony.oscache.base.NeedsRefreshException nre) {
				try {
					neighbors = persistencePlugin.getNeighborhood(context, resource, minDistance, maxNeighbors);
					cache.putInCache(key, neighbors);
					log.debug("Calculate List<Neighbor> with no cache");
			    } 
			    catch (Exception ex) {
			    	neighbors = (List<Neighbor>) nre.getCacheContent();
			        cache.cancelUpdate(key);
			        log.debug("Load List<Neighbor> from Cache after Exception in Calculating Similarity", ex);
			    } 
			}
			return neighbors;
		}
		return null;
	}
	
}
